package cn.sdfykjyxgs.synserver.util;


import cn.sdfykjyxgs.syncommon.entity.FileChangeLog;
import cn.sdfykjyxgs.syncommon.entity.FileChangeLogError;
import cn.sdfykjyxgs.syncommon.mapper.FileChangeLogErrorMapper;
import cn.sdfykjyxgs.syncommon.service.FileChangeLogService;
import cn.sdfykjyxgs.syncommon.socket.WebSocketMessage;
import cn.sdfykjyxgs.synserver.common.WebSocketServer;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.Data;
import net.contentobjects.jnotify.JNotify;
import net.contentobjects.jnotify.JNotifyAdapter;
import net.contentobjects.jnotify.JNotifyException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;


public class JnotifyListening extends JNotifyAdapter {

    FileChangeLogService fileChangeLogService;
    FileChangeLogErrorMapper errorMapper;

    public JnotifyListening(String synPath, FileChangeLogService fileChangeLogService, FileChangeLogErrorMapper errorMapper) {
        this.fileChangeLogService = fileChangeLogService;
        this.errorMapper = errorMapper;
        this.REQUEST_BASE_PATH = synPath;

    }

    public static LinkedHashMap<String, DelKeyInfo> delFileKeys = new LinkedHashMap<>();
    public static ConcurrentHashMap<String, DelKeyInfo> changeFileKeys = new ConcurrentHashMap<>();
    public static ConcurrentHashMap<String, DelKeyInfo> insertFileKeys = new ConcurrentHashMap<>();

    /**
     * 到期时间 毫秒
     */
    private static long expiredTime = 200;
    //可以写到配置文件中
    private static String REQUEST_BASE_PATH = "D:\\test";
    /**
     * 过滤处理的文件格式
     */
    public static List<String> filterFormats = new ArrayList<>();
    Boolean isSaveDataBase = true;
    /**
     * 被监视的目录
     */
    String path = REQUEST_BASE_PATH;
    /**
     * 关注目录的事件
     */
    int mask = JNotify.FILE_CREATED | JNotify.FILE_DELETED | JNotify.FILE_RENAMED | JNotify.FILE_MODIFIED;
    /**
     * 是否监视子目录，即级联监视
     */
    boolean watchSubtree = true;
    /**
     * 监听程序Id
     */
    public int watchID;


    /**
     * 清理过期数据
     */ {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                Iterator<DelKeyInfo> iterator = insertFileKeys.values().iterator();
                while (iterator.hasNext()) {
                    DelKeyInfo next = iterator.next();
                    if (next.getTime() + expiredTime < new Date().getTime()) {
                        iterator.remove();
                    }
                }
            }
        }, 0, 1000);
        //处理错误文件
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                List<FileChangeLogError> fileChangeLogErrors = errorMapper.selectList(new QueryWrapper<>());
                fileChangeLogErrors.forEach(item -> {
                    String url = item.getUrl();
                    String name = item.getName();
                    fileCreated(-1, url, name);
                    errorMapper.deleteById(item.getId());
                });
            }
        }, 1000, 1000);
    }

    /**
     * 容器启动时启动监视程序
     */
    public void beginWatch() {
        /**
         * 添加到监视队列中
         */
        try {
            this.watchID = JNotify.addWatch(path, mask, watchSubtree, this);
            System.err.println("jnotify -----------启动成功2-----------");
        } catch (JNotifyException e) {
            e.printStackTrace();
        }

        while (true) {
            try {
                //主要缓和主线程的执行效率，
                Thread.sleep(1000 * 60);
            } catch (InterruptedException e) {// ignore it
            }
        }
    }

    /**
     * 文件创建
     *
     * @param wd       监听程序Id 初始为1，多个监控程序以此加1
     * @param rootPath 目录名
     * @param name     文件名
     */

    @Override
    public void fileCreated(int wd, String rootPath, String name) {
        boolean b = filterFile(name);
        if (b) return;
        System.err.println(wd + "----->文件被创建, 创建位置为： " + rootPath + "\\" + name);
        if (!isSaveDataBase) return;
        boolean existed = fileChangeLogService.fileExisted(rootPath, name);
//        if (!existed) {
        FileChangeLog fileChangeLog = fileChangeLogService.saveFileLog(rootPath, name);
        String key = fileChangeLog.getKey();
        delFileKeys.remove(key);

        //添加创建文件记录
        insertFileKeys.put(key, new DelKeyInfo(key, new Date().getTime()));

        if (fileChangeLog.getId() != null)
            WebSocketServer.sendMessage(new WebSocketMessage("", "", Arrays.asList(fileChangeLog)));

//        }
//        System.out.println(existed);
    }

    /**
     * 删除文件
     *
     * @param wd       监听程序Id 初始为1，多个监控程序以此加1
     * @param rootPath 目录名
     * @param name     文件名
     */
    @Override
    public void fileDeleted(int wd, String rootPath, String name) {
        boolean b = filterFile(name);
        if (b) return;
        System.err.println(wd + "----->文件被删除, 被删除的文件名为：" + rootPath + name);
        if (!isSaveDataBase) return;
        FileChangeLog fileChangeLog = fileChangeLogService.removeFileLog(rootPath, name);
        //获取文件key
        long time = new Date().getTime();
        delFileKeys.put(fileChangeLog.getKey(), new DelKeyInfo(fileChangeLog.getKey(), time));
        //删除发送消息需要延时
        {
            new Thread() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(expiredTime);
                        DelKeyInfo delKeyInfo = delFileKeys.get(fileChangeLog.getKey());
                        if (delKeyInfo == null) return;
                        if (fileChangeLog.getId() != null) {
                            WebSocketServer.sendMessage(new WebSocketMessage("", "", Arrays.asList(fileChangeLog)));
                            delFileKeys.remove(fileChangeLog.getKey());
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        }
    }

    /**
     * 文件修改 (文件内容被修改和文件夹被修改都可以检测到)
     *
     * @param wd       监听程序Id 初始为1，多个监控程序以此加1
     * @param rootPath 目录名
     * @param name     文件名
     */
    @Override
    public void fileModified(int wd, String rootPath, String name) {
        boolean b = filterFile(name);
        if (b) return;

        if (!isSaveDataBase) return;
        //延时处理 多次修改只做一次记录
        File file = new File(rootPath + "\\" + name);
        String fileKey = fileChangeLogService.getFileKey(file, true);
        changeFileKeys.put(fileKey, new DelKeyInfo(fileKey, 0L));
        {
            new Thread() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(expiredTime);
                        //复制粘贴文件变动处理  如果规定时间内文件存在新增记录则不处理修改操作
                        synchronized (insertFileKeys) {
                            if (insertFileKeys.containsKey(fileKey)) return;
                        }
                        System.err.println(wd + "----->文件内容被修改, 文件名为：" + rootPath + "\\" + name);
                        synchronized (changeFileKeys) {
//                            System.out.println(changeFileKeys.containsKey(fileKey) + "------------进入线程------" + Thread.currentThread().getId());

                            if (changeFileKeys.containsKey(fileKey)) {
                                changeFileKeys.remove(fileKey);
                                FileChangeLog fileChangeLog = fileChangeLogService.fileModifiedLog(rootPath, name);
                                if (fileChangeLog.getId() != null)
                                    WebSocketServer.sendMessage(new WebSocketMessage("", "", Arrays.asList(fileChangeLog)));
//                                System.out.println(fileChangeLog);
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        }

    }

    /**
     * 文件重命名
     *
     * @param wd       监听程序Id 初始为1，多个监控程序以此加1
     * @param rootPath 目录名
     * @param oldName  修改前目录名
     * @param newName  修改后目录名
     */
    @Override
    public void fileRenamed(int wd, String rootPath, String oldName, String newName) {
        boolean b = filterFile(newName);
        if (b) return;
        System.err.println(wd + "----->文件被重命名, 原文件名为：" + rootPath + "\\" + oldName
                + ", 现文件名为：" + rootPath + "\\" + newName);
        if (!isSaveDataBase) return;
        FileChangeLog fileChangeLog = fileChangeLogService.fileRenamedLog(rootPath, newName, oldName);
        if (fileChangeLog.getId() != null)
            WebSocketServer.sendMessage(new WebSocketMessage("", "", Arrays.asList(fileChangeLog)));
    }

    public boolean filterFile(String name) {
        int beginIndex = name.lastIndexOf(".");
        String substring = name.substring(beginIndex == -1 ? 0 : beginIndex);
        boolean contains = filterFormats.contains(substring);
        return contains;
    }
}

@Data
class DelKeyInfo {
    private String key;
    private Long time;

    public DelKeyInfo(String key, Long time) {
        this.key = key;
        this.time = time;
    }
}
